package tools

import (
	"bufio"
	"io"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/tedcy/fdfs_client"
)

// UploadFile 上传图片中间件
// 单路由使用示例：engine.POST("/upload_avatar",tools.UploadFile(),u.updateAvatar)
func UploadFile() gin.HandlerFunc {
	// 中间件返回匿名函数
	return func(c *gin.Context) {
		// 保存目录：image或者avatar或者其他
		// folder := c.Request.PostFormValue("folder")
		folder := c.DefaultPostForm("folder", "image")

		//1. 获取上传的文件(不仅仅是图片)
		file, header, err := c.Request.FormFile("file")
		if err != nil {
			Failed(c, "文件解析失败")
			return
		}
		// TODO: 2. 已登录才支持上传图片

		// 3. 保存file到服务端静态目录
		folderPath := "./static/" + folder
		_, err = os.Stat(folderPath) // 返回文件夹信息和err [(存在:文件夹信息+<nil>), (不存在:<nil>+error)]
		if err != nil {
			// 有错误,且文件夹不存在
			if os.IsNotExist(err) { // 或者os.IsExist(err)
				err = os.Mkdir(folderPath, os.ModePerm)
				if err != nil {
					Failed(c, "目标文件夹创建失败")
					return
				}
			}
		}
		fileFullPath := folderPath + "/" + strconv.FormatInt(time.Now().Unix(), 10) + "_" + header.Filename
		// fmt.Println(fileFullPath) // ./static/avatar/1600963164_Snipaste_2020-06-21_21-58-37.png
		// fmt.Printf("%#v\n", file)// multipart.sectionReadCloser{SectionReader:(*io.SectionReader)(0xc00033bc80)}
		// fmt.Printf("%#v\n", header.Filename) // "Snipaste_2020-06-21_21-58-37.png"
		out, err := os.Create(fileFullPath) // 1.需要`:=`声明新变量out
		// _, err = os.Stat(folderPath)        // 2.没有新变量则不能使用`:=`
		if err != nil {
			Failed(c, "打开目标文件夹失败")
			return
		}
		defer out.Close()
		_, err = io.Copy(out, file)
		if err != nil {
			Failed(c, "文件保存失败")
			return
		}

		// 文件上传成功；往后续中间件传递图片静态资源地址
		// 不要相对路径的点`.`
		c.Set("fileFullPath", fileFullPath[1:])

		// 调用该请求的剩余处理程序,然后再往下执行
		c.Next()
		path := c.MustGet("path").(string)

		Success(c, gin.H{
			"path": path, // 得到后续中间件保存到数据库成功后的地址
		})
		// 不调用该请求的剩余处理程序-终止
		// c.Abort()
	}
}

// UploadFastDFSFile FastDFS
func UploadFastDFSFile() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 保存目录：image或者avatar
		folder := c.DefaultPostForm("folder", "image")

		//1. 获取上传的文件
		file, header, err := c.Request.FormFile("file")
		if err != nil {
			Failed(c, "文件解析失败")
			return
		}
		// TODO: 2. 已登录才支持上传图片

		// 3. 保存file到服务端静态目录
		fileFullPath := "./static/" + folder + "/" + strconv.FormatInt(time.Now().Unix(), 10) + "_" + header.Filename
		out, err := os.Create(fileFullPath)
		if err != nil {
			Failed(c, "打开目标文件夹失败")
			return
		}
		defer out.Close()
		_, err = io.Copy(out, file)
		if err != nil {
			Failed(c, "文件保存失败")
			return
		}
		// 读取FastDFS配置
		fastDFSAddr := "./config/fdfs.conf"
		client, err := fdfs_client.NewClientWithConfig(fastDFSAddr)
		if err != nil {
			Failed(c, "读取fdfs.conf失败")
			return
		}
		defer client.Destory()
		// 将web服务器静态资源内的文件上传至FastDFS,仅传递路径作为参数即可
		fileId, err := client.UploadByFilename(fileFullPath)
		if err != nil {
			Failed(c, "保存文件至fdfs失败")
			return
		}
		// 删除web服务器静态资源内的文件
		os.Remove(fileFullPath)
		c.Set("fileId", fileId)

		// 调用该请求的剩余处理程序,然后再往下执行
		c.Next()
		website := fastDFSServerAddr(fastDFSAddr)
		if website == "" {
			Failed(c, "解析fastDFS配置website失败")
			return
		}
		Success(c, gin.H{
			"path": website + fileId, // 得到后续中间件保存到数据库成功后的地址
		})
		// 不调用该请求的剩余处理程序-终止
		// c.Abort()
	}
}

// fastDFSServerAddr 获取website
// 借鉴：https://github.com/tedcy/fdfs_client/blob/master/config.go
func fastDFSServerAddr(fastDFSAddr string) string {
	file, err := os.Open(fastDFSAddr)
	if err != nil {
		return ""
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	// splitFlag := '\n' | "\n" 两个函数参数单双引号有区分
	result := ""
	for {
		line, err := reader.ReadString('\n') // 一行一行读取：注意是字符
		if err != nil {
			if err == io.EOF { // 读完了
				return result
			}
			return result
		}

		// 去回车符
		line = strings.TrimSuffix(line, "\n")
		// 得到切片：包含=号两边的数据
		str := strings.SplitN(line, "=", 2)
		switch str[0] {
		case "http_server_port":
			result = str[1]
			goto breakTag
		default:
			// 没匹配到，跳出switch
			continue
		}
	}
	// label标签
breakTag:
	return result
}
